package com.ruoyi.common.utils.pdf;

import cn.hutool.core.util.HexUtil;
import com.ruoyi.common.utils.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author
 * @date
 * @description: POI填充word文字和图表
 * <p>
 * 动态添加表格
 */
@Slf4j
public class WordUtil {
    private static String regexStr = "\\$\\{(.+?)\\}";
    private static String tableRegexStr = "<table:(.*?)>";
    private static String deleteRowRegexStr = "<deleteRow>";

    public static String EXPORT_DIR = "/work/pdf/export" + File.separator;
    public static String FONT_FAMILY_PATH = "/work/pdf/fonts/";
    public static String FONT_FAMILY = "SIMKAI.TTF";
    public static int IMAGE_WIDTH = 32;
    public static int IMAGE_HEIGHT = 12;

    private static Map<String, Map<Integer, Integer>> imageConfig = new HashMap<>();

    /**
     * 根据模板生成新word文档
     *
     * @param id           id
     * @param templateName 末班名称
     * @param data         填充模板的数据
     * @param checkBoxList map中checkbox的变量集合
     * @return 返回pdf文件的完整路径
     * @throws Exception
     */
    public static String word2pdf(String templateDir, String templateName, String id, Map<String, Object> data, List<String> checkBoxList) throws Exception {
        XWPFDocument doc = null;
        String templateUrl = templateDir + templateName;
        log.info("---------templateUrl:{}", templateUrl);
        try (FileInputStream in = new FileInputStream(templateUrl)) {
            doc = new XWPFDocument(in);
            close(in);
        } catch (IOException e) {
            log.error("----*****:{}", e);
            return null;
        }

        changeDoc(doc, data, checkBoxList);

        String name = FileUtil.getFileNameUnSuffix(templateName);
        long now = System.currentTimeMillis();

        String zyExportPath = FileUtil.getZyExportDir(EXPORT_DIR, id + File.separator);
        //log.info("zyExportPath:{}", zyExportPath);
        String docFile = FileUtil.getZySaveFilePath(zyExportPath, name, id, now, "docx");
        saveWord(doc, docFile);

        String pdfFile = FileUtil.getZySaveFilePath(zyExportPath, name, id, now, "pdf");
        AsposeUtil.wordTopdf(docFile, pdfFile, FONT_FAMILY_PATH);
        return pdfFile;

    }

    public static String word2pdf(String templateDir, String templateName, String exportDir, String fontDir, String fontFamily, Map<String, Object> data, List<String> checkBoxList) throws Exception {
        XWPFDocument doc = null;
        String templateUrl = templateDir + templateName;
        log.info("---------templateUrl:{}", templateUrl);
        try (FileInputStream in = new FileInputStream(templateUrl)) {
            doc = new XWPFDocument(in);
            close(in);
        } catch (IOException e) {
            log.error("----*****:{}", e);
            return null;
        }

        changeDoc(doc, data, checkBoxList);

        long now = System.currentTimeMillis();
        String name = FileUtil.getFileNameUnSuffix(templateName) + "_" + now;
        String docFile = FileUtil.getFileName(exportDir, name, "docx");
        saveWord(doc, docFile);

        String pdfFile = FileUtil.getFileName(exportDir, name, "pdf");
        AsposeUtil.wordTopdf(docFile, pdfFile, FONT_FAMILY_PATH);
        return pdfFile;
    }

    public static String word2pdf(String templateDir, String templateName, String exportDir, String fontDir, String fontFamily, Map<String, Object> data, List<String> checkBoxList, Map<String, Map<Integer, Integer>> imageConf) throws Exception {
        XWPFDocument doc = null;
        String templateUrl = templateDir + templateName;
        log.info("---------templateUrl:{}", templateUrl);
        try (FileInputStream in = new FileInputStream(templateUrl)) {
            doc = new XWPFDocument(in);
            close(in);
        } catch (IOException e) {
            log.error("----*****:{}", e);
            return null;
        }
        imageConfig = imageConf;
        changeDoc(doc, data, checkBoxList);

        long now = System.currentTimeMillis();
        String name = FileUtil.getFileNameUnSuffix(templateName) + "_" + now;
        String docFile = FileUtil.getFileName(exportDir, name, "docx");
        saveWord(doc, docFile);

        String pdfFile = FileUtil.getFileName(exportDir, name, "pdf");
        AsposeUtil.wordTopdf(docFile, pdfFile, FONT_FAMILY_PATH);
        return pdfFile;
    }

    /**
     * 保存word文件
     *
     * @param document
     * @param docFilePath
     */
    public static void saveWord(XWPFDocument document, String docFilePath) {
        // OK
        OutputStream os = null;
        try {
            os = new FileOutputStream(docFilePath);
            document.write(os);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        close(os);
    }

    /**
     * 填充word模板内容
     *
     * @param document     模板
     * @param map          要替换的信息集合
     * @param checkBoxList map中是复选框的变量值
     * @throws Exception
     */
    public static void changeDoc(XWPFDocument document, Map<String, Object> map, List<String> checkBoxList) throws Exception {
        if (document == null) {
            return;
        }

        handleParagraph(document, map);

        handleTableText(document, map, checkBoxList);
    }

    // 处理段落
    private static void handleParagraph(XWPFDocument document, Map<String, Object> map) {
        if (document == null) {
            return;
        }
        Iterator<XWPFParagraph> itPara = document.getParagraphsIterator();
        while (itPara.hasNext()) {
            XWPFParagraph paragraph = itPara.next();
            String paragraphText = paragraph.getText();
            ParagraphAlignment alignment = paragraph.getAlignment();
            TextAlignment verticalAlignment = paragraph.getVerticalAlignment();

            List<String> varList = varList(paragraphText);
            if (CollectionUtils.isEmpty(varList)) {
                continue;
            }
            for (String var : varList) {
                String newText = getNewContext(paragraphText, var, map.get(var.trim()));
                if (newText != null) {
                    clearParagraphRuns(paragraph);

                    XWPFRun xwpfRun = paragraph.createRun();
                    paragraph.setAlignment(alignment);
                    paragraph.setVerticalAlignment(verticalAlignment);
                    setXWPFRunText(xwpfRun, newText);
                }
            }
        }
    }

    // 处理表格
    private static void handleTableText(XWPFDocument document, Map<String, Object> map, List<String> checkBoxList) {
        Iterator<XWPFTable> itTable = document.getTablesIterator();
        while (itTable.hasNext()) {
            Set<Integer> deleteRowIndexs = new HashSet<>();       //记录删除的行号，从0开始
            XWPFTable table = itTable.next();
            int count = table.getNumberOfRows();
            for (int i = 0; i < count; i++) {
                XWPFTableRow row = table.getRow(i);
                List<XWPFTableCell> cells = row.getTableCells();
                for (int j = 0; j < cells.size(); j++) {
                    XWPFTableCell cell = cells.get(j);
                    String context = cell.getText();

                    // 是否删除行，记录删除行，最后统一删除
                    if (isDeleteRow(context)) {
                        deleteRowIndexs.add(i);
                    }

                    // 判断是否是自动填充表格
                    if (isAutoTable(context)) {
                        // 获取map中自动填充表格数据对象的变量名
                        String rowObjectVar = tableListVar(cell.getText());
                        // 自动填充对象中的变量值
                        List<String> rowDataVars = varList(cell.getText());
                        if (CollectionUtils.isEmpty(rowDataVars)) {
                            break;
                        }

                        //处理自动填充数据表格
                        int addRowCount = insertRowFromCellIndex(table, table.getRow(i), i, j, map.get(rowObjectVar), rowDataVars.get(0));
                        i = i + addRowCount;
                        count = count + addRowCount;
                        break;
                    }

                    List<String> varList = varList(context);        //模板的填充的变量
                    if (CollectionUtils.isEmpty(varList)) {
                        continue;
                    } else {    // 文本 或 图片
                        Set<String> unrepeatVar = new HashSet<>(varList);
                        if (unrepeatVar.size() == 1) {   // 一个单元格设置多个变量，并且需要填充数组的对应值
                            if (checkBoxList.contains(varList.get(0))) {     // checkBox
                                handleCheckBox(cell, map, varList.get(0));
                                continue;
                            } else if (varList.size() > 1) {
                                handleCellArrayValue(cell, varList.get(0), map);
                                continue;
                            }
                        }

                        for (int k = 0; k < varList.size(); k++) {
                            String var = varList.get(k);
                            // 模板中的变量名，不带{}
                            Object dataMapValue = map.get(var.trim());
                            //变量没填充值，需要将文档中变量删除
                            if (dataMapValue == null) {
                                handleCellText(cell, var, "");
                            } else if (dataMapValue instanceof String) {     //字符串
                                handleCellText(cell, var, dataMapValue.toString(), 0);
                            } else if (dataMapValue instanceof List) {        //数组
                                handleCell(cell, var, (List<String>) dataMapValue);
                            }
                        }
                    }
                }// end for
            }// end for

            // 删除行
            deleteTableRow(table, deleteRowIndexs);
        }// end while
    }

    // 删除待删除行，在word模板中使用<deleteRow>进行标记
    private static void deleteTableRow(XWPFTable table, Set<Integer> rowIndexs) {
        int delCount = 0;
        if (CollectionUtils.isEmpty(rowIndexs)) {
            return;
        }
        for (Integer index : rowIndexs) {
            int handleRowIndex = index.intValue() - delCount;       //待处理的行号
            table.removeRow(handleRowIndex);
            delCount++;
        }
    }

    // 清空指定段落中的所有运行元素
    public static void clearParagraphRuns(XWPFParagraph p) {
        for (int i = p.getRuns().size() - 1; i >= 0; i--) {
            p.removeRun(i);
        }
    }


    /**
     * 自动插入行  模板格式:<table:qitiData>${fxsj#dbxqt#fxjg#fxr}
     * @param table
     * @param rowHeader
     * @param mapValue
     * @param newrowIndex
     * @param rowDataVarsStr
     * @return
     */
//    public static int insertRow(XWPFTable table, XWPFTableRow rowHeader, Object mapValue, int newrowIndex, String rowDataVarsStr) {
//        int addRowCount = 0;
//        if (mapValue == null) {
//            return addRowCount;
//        }
//        if (!(mapValue instanceof List)) {
//            return addRowCount;
//        }
//        List<Map<String, String>> rowDataList = (List<Map<String, String>>) mapValue;
//        //log.info("rowDataList.size:{}", rowDataList);
//
//        String[] varArr = rowDataVarsStr.split("#");    //记录自动填充表格的变量值
//        if (varArr == null || varArr.length == 0) {
//            return addRowCount;
//        }
//
//        //遍历表格插入数据
//        for (int rowIndex = 0; rowIndex < rowDataList.size(); rowIndex++) {
//            XWPFTableRow newRow = table.insertNewTableRow(newrowIndex + rowIndex); // 插入新行在表格的最后一行
//            // 复制行样式
//            newRow.getCtRow().setTrPr(rowHeader.getCtRow().getTrPr());
//
//            // 复制单元格（单元格数量必须与原始行相同）
//            for (int i = 0; i < rowHeader.getTableCells().size(); i++) {
//                //log.info("cell--context:{}", rowDataList.get(rowIndex).get(varArr[i]).toString());
//                XWPFTableCell originalCell = rowHeader.getCell(i);
//                XWPFTableCell newCell = newRow.createCell();
//                newCell.removeParagraph(0);
//                XWPFParagraph newParagraph = newCell.addParagraph();
//                String dataValue = "";
//                if (i < varArr.length) {
//                    dataValue = rowDataList.get(rowIndex).get(varArr[i].trim());
//                }
//                dataValue = changeDateStr(dataValue);
//                String imagePath = isImage(dataValue);
//                if (StringUtils.isNotBlank(imagePath)) {
//                    handleImage(newCell, imagePath,  null, true, IMAGE_WIDTH, IMAGE_HEIGHT);
//                } else {
//                    XWPFRun newRun = newParagraph.createRun();
//                    setXWPFRunText(newRun, dataValue);
//                }
//                // 复制单元格样式
//                newCell.getCTTc().setTcPr(originalCell.getCTTc().getTcPr());
//            }
//        }
//
//        // 删除变量行
//        table.removeRow(newrowIndex + rowDataList.size());
//        return rowDataList.size() - 1;
//    }

    /**
     * 在给定的行和列 自动插入行并将给定列后填充字符串
     *
     * @param table
     * @param originRow      插入行参考的原始行样式
     * @param newrowIndex    插入行的索引，从0开始
     * @param cellIndex      数据填充列的索引，从0开始
     * @param mapValue       数据填充值
     * @param rowDataVarsStr 变量名称
     * @return
     */
    public static int insertRowFromCellIndex(XWPFTable table, XWPFTableRow originRow, int newrowIndex, int cellIndex, Object mapValue, String rowDataVarsStr) {
        //log.info("-----newrowIndex:{}, cellIndex:{}", newrowIndex, cellIndex);
        int addRowCount = 0;
        if (mapValue == null || !(mapValue instanceof List) || ((List) mapValue).size() == 0) {
            //清除模板中<table>的内容
            clearRowContextFromCellIndex(originRow, cellIndex);
            return addRowCount;
        }

        List<Map<String, Object>> rowDataList = changeMapValue(mapValue);
        String[] varArr = rowDataVarsStr.split("#");    //记录自动填充表格的变量值
        if (varArr == null || varArr.length == 0) {
            return addRowCount;
        }
        // 去除null值
        removeAllNullValue(rowDataList, varArr);

        //遍历表格插入数据
        for (int rowIndex = 0; rowIndex < rowDataList.size(); rowIndex++) {
            XWPFTableRow newRow = null;
            if (rowIndex == 0) {
                newRow = originRow;
            } else {
                newRow = table.insertNewTableRow(newrowIndex + rowIndex); // 在当前行插入一行
                // 复制行样式
                newRow.getCtRow().setTrPr(originRow.getCtRow().getTrPr());
            }
            // 复制单元格（单元格数量必须与原始行相同）
            for (int i = 0; i < originRow.getTableCells().size(); i++) {
                XWPFTableCell newCell = null;
                XWPFParagraph newParagraph = null;
                XWPFTableCell originalCell = originRow.getCell(i);
                if (rowIndex == 0) {
                    newCell = originalCell;
                } else {
                    newCell = newRow.createCell();
                    // 复制单元格样式
                    newCell.getCTTc().setTcPr(originalCell.getCTTc().getTcPr());
                }

                CTTc ctTc = originalCell.getCTTc();
                // 判断 rowspan 和 colspan 属性是否存在
                if (ctTc.isSetTcPr()) {
                    CTTcPr tcPr = ctTc.getTcPr();
                    if (tcPr.isSetVMerge()) {
                        // 该单元格有合并行
                        CTVMerge vMerge = tcPr.getVMerge();
                        if (STMerge.RESTART == vMerge.getVal()) {
                            vMerge.setVal(STMerge.CONTINUE);
                        }
                    }
                }

                if (i < cellIndex) {
                    continue;
                }
                newCell.removeParagraph(0);
                newParagraph = newCell.addParagraph();
                XWPFRun newRun = newParagraph.createRun();
                Object dataValue = "";
                if (i - cellIndex < varArr.length) {
                    dataValue = rowDataList.get(rowIndex).get(varArr[i - cellIndex].trim());
                }

                if (dataValue == null) {
                    continue;
                } else if (dataValue instanceof String) {     //字符串
                    handleCellText(newCell, null, dataValue.toString(), 0);
                } else if (dataValue instanceof List) {        //数组
                    handleCell(newCell, null, (List<String>) dataValue);
                }
            }
        }
        return rowDataList.size() - 1;
    }

    //清空行的单元格内容
    public static void clearRowContextFromCellIndex(XWPFTableRow row, int cellIndex) {
        //log.info("-------------clearRowContextFromCellIndex");
        for (int i = 0; i < row.getTableCells().size(); i++) {
            if (i < cellIndex) {
                continue;
            }
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                List<XWPFParagraph> paragraphs = cell.getParagraphs();
                if (paragraphs != null && paragraphs.size() > 0) {
                    cell.removeParagraph(0);
                }
            }
        }
    }

    /**
     * 单元格单文本
     *
     * @param cell
     * @param var          map中的变量，不带{}；word文档中变量名，带{}
     * @param dataMapValue
     */
    public static void handleCell(XWPFTableCell cell, String var, List<String> dataMapValue) {
        for (int i = 0; i < dataMapValue.size(); i++) {
            String value = dataMapValue.get(i);
            handleCellText(cell, var, value, i);
        }
    }

    /**
     * 单元格的文本：分为图片和文本内容
     *
     * @param cell
     * @param var          map中的变量，不带{}；word文档中变量名，带{}
     * @param replaceValue
     * @param index
     */
    public static void handleCellText(XWPFTableCell cell, String var, String replaceValue, int index) {
        String imagePath = isImage(replaceValue);
        int width = IMAGE_WIDTH;
        int height = IMAGE_HEIGHT;
        if (StringUtils.isNotBlank(isImage(imagePath))) {
            if (StringUtils.isNoneBlank(var)) {
                if ("zyfwtp".equals(var.trim()) || "syt".equals(var.trim()) || "img1".equals(var.trim())) {
                    width = 240;
                    height = 180;
                }
            }

            if (StringUtils.isNoneBlank(var)) {
                if ("anfileUrl1".equals(var.trim()) || "anfileUrl2".equals(var.trim()) || "anfileUrl3".equals(var.trim())) {
                    width = 88;
                    height = 118;
                }

                if ("anfileUrl4".equals(var.trim()) || "anfileUrl5".equals(var.trim()) || "anfileUrl6".equals(var.trim())) {
                    width = 88;
                    height = 118;
                }
            }

            handleImage(cell, imagePath, var, index == 0, width, height);
        } else {
            replaceValue = changeDateStr(replaceValue);
            handleCellText(cell, var, replaceValue);
        }
    }

    public static void handleCellText(XWPFTableCell cell, String var, String replaceValue) {
        replaceValue = changeDateStr(replaceValue);

        if (StringUtils.isNotBlank(var)) {
            String newContext = getNewContext(cell.getText(), var, replaceValue);
            XWPFParagraph paragraph = cell.getParagraphs().get(0);
            ParagraphAlignment alignment = paragraph.getAlignment();
            TextAlignment verticalAlignment = paragraph.getVerticalAlignment();
            cell.removeParagraph(0);
            XWPFParagraph newParagraph = cell.addParagraph();
            newParagraph.setAlignment(alignment);
            newParagraph.setVerticalAlignment(verticalAlignment);
            XWPFRun newRun = newParagraph.createRun();
            setXWPFRunText(newRun, newContext);
        } else {
            XWPFRun newRun = cell.getParagraphs().get(0).getRuns().get(0);
            setXWPFRunText(newRun, replaceValue);
        }
    }

    /**
     * @param var 数据的变量名称，不带 ${}
     *            处理单元格中有相同变量填充数组的值情况
     */
    public static void handleCellArrayValue(XWPFTableCell cell, String var, Map<String, Object> map) {
        //String dataMapKey = getVarParam(var);
        Object dataMapValue = map.get(var.trim());
        String newContext = cell.getText();

        if (dataMapValue == null) {
            newContext = replaceText(newContext, getVarParam(var), "");
        } else if (dataMapValue instanceof String) {
            newContext = replaceFirstText(newContext, getVarPatternParam(var), dataMapValue.toString());
        } else if (dataMapValue instanceof List) {
            for (int i = 0; i < ((List<String>) dataMapValue).size(); i++) {
                String value = ((List<String>) dataMapValue).get(i);

                value = changeDateStr(value);
                newContext = replaceFirstText(newContext, getVarPatternParam(var), value);
            }
        }

        ParagraphAlignment alignment = cell.getParagraphs().get(0).getAlignment();
        TextAlignment verticalAlignment = cell.getParagraphs().get(0).getVerticalAlignment();
        cell.removeParagraph(0);
        XWPFParagraph newParagraph = cell.addParagraph();
        newParagraph.setAlignment(alignment);
        newParagraph.setVerticalAlignment(verticalAlignment);
        // 填充新内容
        XWPFRun newRun = newParagraph.createRun();
        setXWPFRunText(newRun, newContext);
    }

    // 是否是图片
    private static boolean isImage2(String value) {
        if (StringUtils.isEmpty(value)) {
            return false;
        }
        if (value.endsWith(".jpg") || value.endsWith(".png") || value.endsWith(".jpeg")) {//填充表格中的图片
            return true;
        }
        // 解密
        try {
            if (value.length() > 80) {
                String decodeStr = HexUtil.decodeHexStr(value);
                if (decodeStr.endsWith(".jpg") || decodeStr.endsWith(".png") || decodeStr.endsWith(".jpeg")) {
                    return true;
                }
            }
        } catch (Exception e) {

        }
        return false;
    }

    // 是否是图片
    private static String isImage(String value) {
        if (StringUtils.isEmpty(value)) {
            return null;
        }
        if (value.endsWith(".jpg") || value.endsWith(".png") || value.endsWith(".jpeg")) {//填充表格中的图片
            return value;
        }
        // 解密
        try {
            if (value.length() > 80) {
                String decodeStr = HexUtil.decodeHexStr(value);
                if (decodeStr.endsWith(".jpg") || decodeStr.endsWith(".png") || decodeStr.endsWith(".jpeg")) {
                    return decodeStr;
                }
            }
        } catch (Exception e) {

        }
        return null;
    }

    // 处理图片
    private static void handleImage(XWPFTableCell cell, String path, String varName, boolean init, int width, int height) {
        if (cell == null) {
            return;
        }
        List<XWPFParagraph> paragraphs = cell.getParagraphs();
        for (int i = 0; i < paragraphs.size(); i++) {
            XWPFParagraph paragraph = paragraphs.get(i);
            String text = paragraph.getText().trim();

            /*if (!containVar(text)  && !StringUtils.isBlank(varName)) { //自动填充表格中的变量都在第一行中给出，varName是null
                continue;
            }*/
            try {
                URL url = new URL(path);
                InputStream imageInputStream = url.openStream();
                byte[] byteArray = IOUtils.toByteArray(imageInputStream);
                String newText = getNewContext(text, varName, "");
                if (init) { //初始加载第一张图片
                    clearParagraphRuns(paragraph);
                    XWPFRun textRun = paragraph.createRun();
                    setXWPFRunText(textRun, newText);
                    paragraph.addRun(textRun);
                    XWPFRun imageCellRun = paragraph.createRun();
                    imageCellRun.addPicture(new ByteArrayInputStream(byteArray), Document.PICTURE_TYPE_JPEG, "", Units.pixelToEMU(width), Units.pixelToEMU(height));
                } else {     //追加图片
                    //paragraph = cell.getParagraphs().get(0);
                    //XWPFRun imageCellRun = paragraph.createRun();
                    XWPFRun imageCellRun = paragraph.createRun();
                    imageCellRun.addPicture(new ByteArrayInputStream(byteArray), Document.PICTURE_TYPE_JPEG, "", Units.pixelToEMU(width), Units.pixelToEMU(height));
                }
            } catch (Exception e) {
                // 读取图片异常时，用空白填充
                handleCellText(cell, varName, "");
                log.error("image error:", e);
            }
        }
    }

    /**
     * @param cell 单元格
     * @param map  变量对应的值 <模板变量, 变量值>
     * @param var  文档模板单元格中的变量
     */
    private static void handleCheckBox(XWPFTableCell cell, Map<String, Object> map, String var) {
        if (cell == null) {
            return;
        }

        List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
        int pSize = cellParagraphs.size();
        for (int i = 0; i < pSize; i++) {
            XWPFParagraph p = cellParagraphs.get(i);
            if (p == null || p.getRuns().size() == 0) {
                break;
            }

            String cellParaText = p.getText();
            String[] list = cellParaText.trim().split(regexStr);
            if (list == null || list.length == 0) {
                return;
            }

            String varParam = getVarPatternParam(var);
            ;
            StringBuffer strBuffer = new StringBuffer();
            //log.info("---image--var:{}, map.containsKey(var.trim()):{}", var, map.containsKey(var.trim()));
            if (!map.containsKey(var.trim())) {     // map中无变量的值
                // 去掉变量
                strBuffer.append(getUncheckText(cellParaText, varParam));
            } else {
                Object selectValues = map.get(var.trim());
                if (Objects.isNull(selectValues)) {     // map中变量值是null
                    // 去掉变量
                    strBuffer.append(getUncheckText(cellParaText, varParam));
                }
                if (selectValues instanceof String) {       // map中变量值是字符串
                    String selectValue = selectValues.toString().trim();
                    List<String> values = new ArrayList<>();
                    values.add(selectValue);
                    strBuffer.append(getCheckText(list, values));
                } else if (selectValues instanceof List) {  // map中变量值是List
                    strBuffer.append(getCheckText(list, (List<String>) selectValues));
                }
            }

            cell.removeParagraph(i);        //移除

            String newContext = strBuffer.toString();
            //log.info("var:{}-------------newContext:{}", var, newContext);
            XWPFRun xwpfRun = cell.addParagraph().createRun();
            setXWPFRunText(xwpfRun, newContext);
        }
    }

    // 设置字体


    public static void setXWPFRunText(XWPFRun run, String context) {
        run.setText(context);
        run.setFontFamily(FONT_FAMILY_PATH + FONT_FAMILY);
        run.setFontSize(9);
    }

    /**
     * 设置文本框的选中和非选中状态
     *
     * @param list         文档模板中checkbox对应的选项内容集合
     * @param selectValues 选中的值
     * @return
     */
    public static StringBuffer getCheckText(String[] list, List<String> selectValues) {
        StringBuffer strBuffer = new StringBuffer();
        if (Objects.isNull(selectValues)) {
            return strBuffer;
        }

        List<String> selectItems = new ArrayList<>();
        for (String str : selectValues) {
            selectItems.add(str.trim().replace(" ", ""));
        }

        for (String docItem : list) {
            if (StringUtils.isEmpty(docItem)) {
                continue;
            }
            String context = getCheckItemByContext(docItem, selectItems);
            strBuffer.append(context);
        }
        return strBuffer;
    }

    /**
     * 选中/未选中
     *
     * @param docItem     文档中的选项内容
     * @param selectItems 选中的选项内容集合
     * @return
     */
    public static String getCheckItemByContext(String docItem, List<String> selectItems) {
        String item = docItem.trim().replace(" ", "");
        if (selectItems.contains(item)) {
            return "√ " + docItem;
        } else {
            return "\u2610" + docItem;
        }
    }

    public static String getUncheckText(String cellContext, String varKey) {
        //String text = cellContext.replaceAll(varKey, "□");
        String text = cellContext.replaceAll(varKey, "\u2610");
        return text;
    }

    /*填充字符串文本
    dataMapKey: map中变量值，不带{}
     */
    private static String getNewContext(String context, String dataMapKey, Object dataMapValue) {
        String newContext = null;
        String docVar = getVarParam(dataMapKey);

        if (dataMapValue == null) {
            newContext = replaceText(context, docVar, "");
        } else if (dataMapValue instanceof String) {     //字符串
            String value = changeDateStr(dataMapValue.toString());
            newContext = replaceText(context, docVar, value);
        } else if (dataMapValue instanceof List) {        //数组
            newContext = context;
            for (String mapValue : (List<String>) dataMapValue) {
                String value = changeDateStr(mapValue);
                newContext = replaceText(newContext, docVar, value);
            }
        }
        //log.info("newContext:{}", newContext);
        return newContext;
    }

    public static String replaceText(String originStr, String varName, String varValue) {
        if (StringUtils.isBlank(originStr)) {
            return "";
        }
        String newText = originStr.replace(varName, varValue);
        return newText;
    }

    public static String replaceFirstText(String originStr, String varName, String varValue) {
        if (StringUtils.isBlank(originStr)) {
            return "";
        }
        String newText = originStr.replaceFirst(varName, varValue);
        //log.info("newText:{}", newText);
        return newText;
    }

    public static String getVarPatternParam(String var) {
        String docVar = "\\$\\{" + var + "\\}";
        ;
        return docVar;
    }

    public static String getVarParam(String var) {
        String docVar = "${" + var + "}";
        return docVar;
    }

    // 判断该行是否是自动填充表格
    public static boolean isAutoTable(String str) {
        Pattern pattern = Pattern.compile(tableRegexStr);
        Matcher matcher = pattern.matcher(str);
        return matcher.find();
    }

    // 获取自动填充行的变量名
    public static String tableListVar(String str) {
        Pattern pattern = Pattern.compile(tableRegexStr);
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    //判断是否要删除的行，为了适应自动填充行有合并行的操作，如果有合并行，自动填充的首行保留为空，不填充内容，最后删除空行
    public static boolean isDeleteRow(String str) {
        Pattern pattern = Pattern.compile(deleteRowRegexStr);
        Matcher matcher = pattern.matcher(str);
        return matcher.find();
    }

    // 时间字符串转换
    public static String changeDateStr(String str) {
        if (StringUtils.isBlank(str)) {
            return "";
        }
        if (!DateUtil.isDateTime(str)) {
            return str;
        }
        return DateUtil.date2cnFormat(str);
    }

    public static boolean containVar(String context) {
        if (StringUtils.isEmpty(context)) {
            return false;
        }
        Pattern pattern = Pattern.compile(regexStr);
        Matcher matcher = pattern.matcher(context.trim());
        return matcher.find();
    }

    private static List<String> varList(String context) {
        List<String> varList = new ArrayList<>();
        if (StringUtils.isEmpty(context)) {
            return varList;
        }

        Pattern pattern = Pattern.compile(regexStr);
        Matcher matcher = pattern.matcher(context);

        while (matcher.find()) { // 查找所有匹配项
            String matchedString = matcher.group(1); // 去掉${ } 获取匹配到的字符串
            varList.add(matchedString);
        }
        return varList;
    }

    public static List<Map<String, Object>> changeMapValue(Object mapValue) {
        List<Map<String, Object>> list = new ArrayList<>();
        if (!Objects.isNull(mapValue)) {
            if (mapValue instanceof List) {
                list = (List<Map<String, Object>>) mapValue;
            }
        }
        return list;
    }

    // 去掉全为null的值
    public static void removeAllNullValue(List<Map<String, Object>> list, String[] varArr) {
        Iterator<Map<String, Object>> it = list.iterator();
        while (it.hasNext()) {
            int nullValueCount = 0;
            Map<String, Object> map = it.next();
            for (String key : varArr) {
                Object dataValue = map.get(key);
                if (Objects.isNull(dataValue)) {
                    nullValueCount++;
                }
            }
            if (nullValueCount == varArr.length) {
                it.remove();
            }
        }
    }

    /**
     * 关闭输入流
     *
     * @param is
     */
    public static void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭输出流
     *
     * @param os
     */
    public static void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
